﻿using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpFont;
using System.Diagnostics;
using Emgu.CV.Util;

namespace QRCodeNet
{
	public static class ExceptionDealer
	{
		public static void ShowException(Exception e)
		{
			Console.WriteLine($"发生 {e.Message} 异常");
			Console.WriteLine(e.StackTrace);
		}
	}
}

namespace Emgu.CV.Extension
{
	/*
	public class MatPositionDesc
	{
		public Mat mat;
		public int xOffset, yOffset;
	}
	*/
	public static class CvInvoke
	{
		private static Bitmap RenderString(Library library, SharpFont.Face face, string text, Color foreColor, Color backColor)
		{
			float penX = 0, penY = 0;
			float stringWidth = 0, stringHeight = 0;

			float top = 0, bottom = 0;
			for (int i = 0; i < text.Length; i++)
			{
				//load character
				char c = text[i];
				uint glyphIndex = face.GetCharIndex(c);
				face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
				float AdvanceX = (float)face.Glyph.Advance.X;
				float BearingX = (float)face.Glyph.Metrics.HorizontalBearingX;
				float Width = face.Glyph.Metrics.Width.ToSingle();

				//underrun/overrun

				//Top/Bottom
				float glyphTop = (float)face.Glyph.Metrics.HorizontalBearingY;
				float glyphBottom = (float)face.Glyph.Metrics.Height - glyphTop;
				if (glyphTop > top)
					top = glyphTop;
				if (glyphBottom > bottom)
					bottom = glyphBottom;
				stringWidth += AdvanceX;
			}
			stringHeight = top + bottom;
			if (stringHeight == 0 || stringWidth == 0)
				return null;
			Bitmap bmp = new Bitmap((int)Math.Ceiling(stringWidth), (int)Math.Ceiling(stringHeight));
			stringWidth = 0;
			using (var g = Graphics.FromImage(bmp))
			{
				//set up graphics
				g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
				g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
				g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
				g.Clear(backColor);

				//draw string into bmp
				for (int i = 0; i < text.Length; i++)
				{
					//load char
					char c = text[i];
					uint glyphIndex = face.GetCharIndex(c);
					face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
					face.Glyph.RenderGlyph(RenderMode.Normal);
					FTBitmap ftbmp = face.Glyph.Bitmap;
					float AdvanceX = (float)face.Glyph.Advance.X;
					float BearingX = (float)face.Glyph.Metrics.HorizontalBearingX;
					float Width = face.Glyph.Metrics.Width.ToSingle();

					//underrun

					//draw glyph
					if (ftbmp.Width > 0 && ftbmp.Rows > 0)
					{
						Bitmap cBmp = ftbmp.ToGdipBitmap(foreColor);
						int x = (int)(penX + face.Glyph.BitmapLeft);
						int y = (int)(penY + top - (float)face.Glyph.Metrics.HorizontalBearingY);
						g.DrawImageUnscaled(cBmp, x, y);
					}
					else
					{
						//
					}

					//overrun

					penX += (float)face.Glyph.Advance.X;
					penY += (float)face.Glyph.Advance.Y;
				}
			}
			return bmp;
		}
		private static ColorConversion GetConversionCode(int srcChan, int dstChan)
		{
			if (dstChan == 1)
				return ColorConversion.Bgra2Gray;
			else if (dstChan == 3)
				return ColorConversion.Bgra2Bgr;
			else
				throw new Exception("cannot convert type (int putTextW)");
		}
		private static void Blend(Image<Bgra, byte> png, IInputArray back, IOutputArray dst)
		{
			VectorOfMat tmp = new VectorOfMat();
			Image<Gray, byte>[] chan = png.Split();
			Image<Gray, byte> mask = chan[3];
			for (int i = 0; i < 3; i++)
				tmp.Push(mask.Mul(chan[i], 1.0 / 255));
			tmp.Push(new Image<Gray, byte>(png.Width, png.Height, new Gray(255)));
			Mat src1 = new Mat();
			CV.CvInvoke.Merge(tmp, src1);

			Image<Bgra, byte> B = back.GetInputArray().GetMat().ToImage<Bgra, byte>();
			chan = B.Split();
			tmp = new VectorOfMat();
			for (int i = 0; i < 3; i++)
				tmp.Push((255 - mask).Mul(chan[i], 1.0 / 255));
			tmp.Push(new Image<Gray, byte>(png.Width, png.Height, new Gray(255)));//same size
			Mat src2 = new Mat();
			CV.CvInvoke.Merge(tmp, src2);
			Mat Fore = src1, Back = src2;
			if (src1.NumberOfChannels != dst.GetOutputArray().GetChannels())
				CV.CvInvoke.CvtColor(src1, Fore, GetConversionCode(src1.NumberOfChannels, dst.GetOutputArray().GetChannels()));
			if (src2.NumberOfChannels != dst.GetOutputArray().GetChannels())
				CV.CvInvoke.CvtColor(src2, Back, GetConversionCode(src1.NumberOfChannels, dst.GetOutputArray().GetChannels()));

			CV.CvInvoke.Add(Fore, Back, dst);
		}
		public static void PutText(IInputOutputArray img, string text, Point org, double fontScale, Color color)
		{
			Library lib = new Library();
			SharpFont.Face face = new SharpFont.Face(lib, @"fonts\simfang.ttf");
			face.SetCharSize(0, 12.0 * fontScale, 0, 96);
			Bitmap strBmp = RenderString(lib, face, text, color, Color.Transparent);
			if (strBmp == null) return;
			Mat Mstr = new Image<Bgra, byte>(strBmp).Mat, dst = new Mat();
			Mat roi = new Mat(img.GetInputArray().GetMat(), new Rectangle(org, Mstr.Size));
			Blend(Mstr.ToImage<Bgra, byte>(), roi, roi);
		}

		/*
		/// <summary>
		/// 将输入图像按行列分割为小图。
		/// </summary>
		/// <param name="input">输入图像</param>
		/// <param name="xOffset">左上角起点x轴</param>
		/// <param name="yOffset">左上角起点y轴</param>
		/// <param name="eachHei">每个小图高度</param>
		/// <param name="eachWid">每个小图宽度</param>
		/// <param name="vCount">行数</param>
		/// <param name="hCount">列数</param>
		/// <returns>小图</returns>
		public static List<MatPositionDesc> Partion(IInputArray input, int xOffset, int yOffset, int eachHei, int eachWid, int vCount, int hCount)
		{


		}
		*/
	}

}
